﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson14
{
    #region 协变逆变

    // 协变
    // 和谐的变化，自然的变化
    // 因为里氏替换原则  父类可以装子类
    // 所以子类变父类感觉是和谐的， 比如 string变object

    // 协变：可以把子类的对象赋值给父类的变量

    // 逆变
    // 逆常规的变化，不正常的变化
    // 父类可以装子类，但子类不能装父类
    // 所以父类变子类感觉是不和谐的， 比如 object变string

    // 逆变：可以把父类的对象赋值给子类的变量

    // 协变和逆变是用来修饰泛型的
    // 关键字
    // 协变： out
    // 逆变： in
    // 用于在泛型中修饰 泛型字母的
    // 只有泛型接口和泛型委托才可以使用协变和逆变

    // 作用
    // 用out修饰的泛型，只能作为返回值使用
    delegate T TestOutFunc<out T>(); // 用out修饰的泛型，只能作为返回值使用

    // 用in修饰的泛型，只能作为参数使用
    delegate void TestInFunc<in T>(T t); // 用in修饰的泛型，只能作为参数使用

    // 结合里氏替换原则理解
    class Father
    {
    }
    class Son : Father
    {
    }

    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            // 作用，里氏替换原则的例子
            // 协变
            // 父类总是能被子类替换
            TestOutFunc<Son> os = () =>
            {
                return new Son();
            };
            // 如果委托没有 out 是不能赋值的， 加了 out 之后 可以赋值，会自己判断是否有继承关系
            TestOutFunc<Father> of = os; // 协变，父类可以被子类替换,看起来Son变成Father了，
            Father f = of();  // 这里是调用了os的返回值，实际上返回的是Son

            // 逆变
            TestInFunc<Father> tif = (value) =>
            {
                Console.WriteLine("Father");
            };
            // 如果委托没有 in 是不能赋值的， 加了 in 之后 可以赋值
            TestInFunc<Son> tis = tif; // 逆变，子类可以被父类替换

            tis(new Son()); // 传入的是Son，实际上调用的是 Father的函数
        }
    }
}
